home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Games of Daze
/
Infomagic - Games of Daze (Summer 1995) (Disc 1 of 2).iso
/
x2ftp
/
msdos
/
misc
/
moonbas2
/
moon.cpp
< prev
next >
Wrap
C/C++ Source or Header
|
1995-04-11
|
25KB
|
871 lines
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>
#include <ctype.h>
#include <time.h>
#include <dos.h>
#include <mem.h>
#include "random.h"
#include "boolean.h"
#include "fixed.h"
#include "vga.h"
#include "mouse.h"
#include "world.h"
//#define DEBUG
#ifdef DEBUG
static void _Assert
(
const char * file,
unsigned line,
const char * assertion
)
{
vga.end ();
fflush (stdout);
fprintf ( stderr, "\nAssertion failed (%s, line %u): %s\n",
file, line, assertion );
fflush (stderr);
abort ();
}
#define ASSERT(f) if (f) {} else _Assert ( __FILE__, __LINE__, #f )
#else
#define ASSERT(f)
#endif
//----------------------------------------------------------------------------
const view_size = 64;
const view_start = view_size / 2;
const win_cen_x = 160;
const win_cen_y = 100;
const black = 0;
const cursor_color = 255;
enum key_t
{
esc_key = 27,
home_key = 71,
up_arrow_key = 72,
pgup_key = 73,
lt_arrow_key = 75,
center_key = 76,
rt_arrow_key = 77,
end_key = 79,
dn_arrow_key = 80,
pgdn_key = 81
};
//----------------------------------------------------------------------------
// The viewer's position variables.
//----------------------------------------------------------------------------
struct viewer_t
{
int x;
int y;
int z;
angle_t angle;
fixed sine;
fixed cosine;
};
static viewer_t viewer;
//----------------------------------------------------------------------------
// The view window.
//----------------------------------------------------------------------------
static win_t win =
{
0, 0, 320, 200
};
//----------------------------------------------------------------------------
// The column data structures used by draw_row.
//----------------------------------------------------------------------------
struct col_t
{
int y;
unsigned char color;
};
static col_t col [ 321 ];
static col_t old_col [ 321 ];
//----------------------------------------------------------------------------
// FUNCTION mul_and_div
//----------------------------------------------------------------------------
// This calculates (a * b) / c with increased precision by using all 64 bits
// from the multiplication in calculating the result of the division.
//----------------------------------------------------------------------------
int mul_and_div ( int a, int b, int c );
#pragma aux mul_and_div = \
"imul ebx" \
"idiv ecx" \
parm caller [eax] [ebx] [ecx] \
value [eax] \
modify [eax ebx ecx edx];
//----------------------------------------------------------------------------
// FUNCTION dot_product
//----------------------------------------------------------------------------
//
// dot_product = (u1 * v1 + u2 * v2) / 2^16;
//
// This is designed to be used with integer sine and cosine values in the
// range -2^16..2^16; hence the division by 2^16 following the dot product.
// Using this function improves precision since it has a 64-bit addition.
//
//----------------------------------------------------------------------------
int dot_product ( int u1, int u2, int v1, int v2 );
#pragma aux dot_product = \
"imul edx" \
"mov esi, edx" \
"mov edi, eax" \
"mov eax, ebx" \
"imul ecx" \
"add eax, edi" \
"adc edx, esi" \
"shrd eax, edx, 16" \
parm caller [eax] [ebx] [edx] [ecx] \
value [eax] \
modify [eax ebx ecx edx esi edi];
//----------------------------------------------------------------------------
// FUNCTION view_overhead_map
//----------------------------------------------------------------------------
static void view_overhead_map
(
void
)
{
int start_x = (int) (viewer.x / scale_area) - win_cen_x;
int start_y = (int) (viewer.y / scale_area) - win_cen_y;
start_x &= clip_mask_x;
start_y &= clip_mask_y;
// Draw the map, with the viewer's position in the center.
const sequ_addr = 0x3C4;
const bytes_per_row = 80;
extern char * a_page;
for ( int plane = 0; plane < 4; ++ plane )
{
outpw ( sequ_addr, 0x02 | (0x100 << plane) );
int map_x = start_x;
for ( int x = 0; x < bytes_per_row; ++ x )
{
int map_y = start_y;
char * pixel_p = a_page + x;
char * stop_p = pixel_p + bytes_per_row * 200;
for ( ; pixel_p < stop_p; pixel_p += bytes_per_row )
{
*pixel_p = color_map [map_x][map_y];
map_y = (map_y + 1) & clip_mask_y;
}
map_x = (map_x + 4) & clip_mask_x;
}
start_x = (start_x + 1) & clip_mask_x;
}
// Draw a line indicating the viewer's heading.
int x1 = dot_product ( viewer.sine, viewer.cosine, 20, 0 ) + 160;
int y1 = dot_product ( -viewer.cosine, viewer.sine, 20, 0 ) + 100;
int x2 = dot_product ( viewer.sine, viewer.cosine, 30, 0 ) + 160;
int y2 = dot_product ( -viewer.cosine, viewer.sine, 30, 0 ) + 100;
win.line ( x1, y1, x2, y2, cursor_color );
// Draw a marker at the center indicating the viewer's position.
win.rect ( 159, 99, 162, 102, black );
win.point ( 160, 100, cursor_color );
}
//----------------------------------------------------------------------------
//
// The graphics workhorse routines.
//
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// FUNCTION calc_color
//----------------------------------------------------------------------------
inline unsigned char calc_color
(
int map_x,
int map_y
)
{
return color_map [map_x][map_y];
}
//----------------------------------------------------------------------------
// FUNCTION draw_row
//----------------------------------------------------------------------------
static void draw_row
(
void
)
{
const sequ_addr = 0x3C4;
const bytes_per_row = 80;
extern char * a_page;
int x;
for ( x = 0; x < 320; ++ x )
{
if ( col [x].y < 0 ) col [x].y = 0;
else if ( col [x].y > 200 ) col [x].y = 200;
}
for ( int plane = 0; plane < 4; ++ plane )
{
outpw ( sequ_addr, 0x02 | (0x100 << plane) );
int col_x = plane;
for ( x = 0; x < bytes_per_row; ++ x )
{
ASSERT ( old_col [col_x].y >= 0 );
ASSERT ( old_col [col_x].y <= 200 );
ASSERT ( col [col_x].y >= 0 );
ASSERT ( col [col_x].y <= 200 );
char * pixel_p = & a_page [bytes_per_row*old_col [col_x].y + x];
char * stop_p = & a_page [bytes_per_row* col [col_x].y + x];
int color = old_col [col_x].color;
int den = col [col_x].y - old_col [col_x].y;
int num = col [col_x].color - color;
int color_inc;
if ( num < 0 ) { color_inc = -1; num = -num; }
else color_inc = 1;
int err = 0;
while ( pixel_p < stop_p )
{
*pixel_p = (unsigned char) color;
pixel_p += bytes_per_row;
err += num;
while ( err >= den )
{
err -= den;
color += color_inc;
}
}
old_col [col_x].y = col [col_x].y;
old_col [col_x].color = col [col_x].color;
col_x += 4;
}
}
}
//----------------------------------------------------------------------------
// FUNCTION rotate
//----------------------------------------------------------------------------
// Takes an (x, y) coordinate pair and rotates it using the variables
// sine and cosine, which are 32-bit integer values ranging from -65536 to
// 65536.
//----------------------------------------------------------------------------
inline void rotate
(
int & x,
int & y,
int sine,
int cosine
)
{
int temp_x = x;
int temp_y = y;
x = dot_product ( cosine, -sine, temp_x, temp_y );
y = dot_product ( sine, cosine, temp_x, temp_y );
}
//----------------------------------------------------------------------------
// FUNCTION view_3d
//----------------------------------------------------------------------------
static void view_3d
(
void
)
{
const sz = 240; // Dist. from viewer's eye to screen in pixels
// Map coordinate system setup:
int viewer_map_x = viewer.x / scale_area;
int viewer_map_y = viewer.y / scale_area;
int map_start_x, map_start_y;
int map_start_x_inc, map_start_y_inc;
int map_x_inc, map_y_inc;
int start_x, start_y;
if ( viewer.angle < 450 || viewer.angle >= 3150 )
{
map_start_x = (viewer_map_x - view_start) & clip_mask_x;
map_start_y = (viewer_map_y - view_start) & clip_mask_y;
map_start_x_inc = 0;
map_start_y_inc = 1;
map_x_inc = 1;
map_y_inc = 0;
start_x = -view_start * scale_area;
start_y = view_start * scale_area;
}
else if ( viewer.angle < 1350 )
{
map_start_x = (viewer_map_x + view_start) & clip_mask_x;
map_start_y = (viewer_map_y - view_start) & clip_mask_y;
map_start_x_inc = -1;
map_start_y_inc = 0;
map_x_inc = 0;
map_y_inc = 1;
start_x = view_start * scale_area;
start_y = view_start * scale_area;
}
else if ( viewer.angle < 2250 )
{
map_start_x = (viewer_map_x + view_start) & clip_mask_x;
map_start_y = (viewer_map_y + view_start) & clip_mask_y;
map_start_x_inc = 0;
map_start_y_inc = -1;
map_x_inc = -1;
map_y_inc = 0;
start_x = view_start * scale_area;
start_y = -view_start * scale_area;
}
else
{
map_start_x = (viewer_map_x - view_start) & clip_mask_x;
map_start_y = (viewer_map_y + view_start) & clip_mask_y;
map_start_x_inc = 1;
map_start_y_inc = 0;
map_x_inc = 0;
map_y_inc = -1;
start_x = -view_start * scale_area;
start_y = -view_start * scale_area;
}
// World coordinate system setup:
start_x -= (viewer.x % scale_area);
start_y += (viewer.y % scale_area);
int world_x_inc = map_x_inc * scale_area;
int world_y_inc = -map_y_inc * scale_area;
rotate ( start_x, start_y, viewer.sine, viewer.cosine );
rotate ( world_x_inc, world_y_inc, viewer.sine, viewer.cosine );
int start_x_inc = world_y_inc;
int start_y_inc = -world_x_inc;
// Draw rows:
vga.clear ( 0 );
for ( int x = 0; x < 320; ++ x )
{
old_col [x].y = 200;
old_col [x].color = 0;
col [x].y = 200;
col [x].color = 0;
}
for ( int nr_rows = view_size / 2; nr_rows > 0; -- nr_rows )
{
int map_x = map_start_x;
int map_y = map_start_y;
int world_x = start_x;
int world_y = start_y;
int world_z = viewer.z - alt_map [map_x][map_y] * scale_height;
int last_sx = 320;
int last_sy = 0;
int last_color = 0;
int nr_cols = view_size;
while ( nr_cols > 0 )
{
// Calculate the screen coordinates.
if ( world_y > 0 )
{
int sx = mul_and_div ( world_x, sz, world_y ) + win_cen_x;
int sy = mul_and_div ( world_z, sz, world_y ) + win_cen_y;
int color;
color = calc_color ( map_x, map_y );
// Draw if the point is onscreen.
if ( sx > win.x1 )
{
int den = sx - last_sx;
int num = color - last_color;
int color_inc;
if ( num < 0 ) { color_inc = -1; num = -num; }
else color_inc = 1;
int err = 0;
int num2 = sy - last_sy;
int last_sy_inc;
if ( num2 < 0 ) { last_sy_inc = -1; num2 = -num2; }
else last_sy_inc = 1;
int err2 = 0;
// Left edge clipping:
if ( last_sx < win.x1 )
{
int delta = win.x1 - last_sx;
err = num * delta % den;
last_color += color_inc * num * delta / den;
err2 = num2 * delta % den;
last_sy += last_sy_inc * num2 * delta / den;
last_sx = win.x1;
}
// Right edge clipping:
if ( sx > win.x2 )
sx = win.x2;
// Draw the section:
for ( int x = last_sx; x < sx; ++ x )
{
ASSERT ( x >= 0 && x < 320 );
col [x].y = last_sy;
col [x].color = (unsigned char) last_color;
err += num;
while ( err >= den )
{
err -= den;
last_color += color_inc;
}
err2 += num2;
while ( err2 >= den )
{
err2 -= den;
last_sy += last_sy_inc;
}
}
if ( sx == win.x2 )
break;
}
last_sx = sx;
last_sy = sy;
last_color = color;
}
// Go to the next point.
map_x = (map_x + map_x_inc) & clip_mask_x;
map_y = (map_y + map_y_inc) & clip_mask_y;
world_x += world_x_inc;
world_y += world_y_inc;
world_z = viewer.z - alt_map [map_x][map_y] * scale_height;
-- nr_cols;
}
// Draw this row of points.
draw_row ();
// Move the starting position to the next row.
start_x += start_x_inc;
start_y += start_y_inc;
map_start_x = (map_start_x + map_start_x_inc) & clip_mask_x;
map_start_y = (map_start_y + map_start_y_inc) & clip_mask_y;
}
// Finish the drawing by drawing down to the bottom edge of the screen.
for ( x = 0; x < 320; ++ x )
{
col [x].y = win.y2;
col [x].color = calc_color (viewer_map_x, viewer_map_y);
}
draw_row ();
}
//----------------------------------------------------------------------------
// FUNCTION set_palette
//----------------------------------------------------------------------------
struct color_t
{
char r;
char g;
char b;
};
static void set_palette
(
void
)
{
const DAC_write_index = 0x3C8;
const DAC_data_index = 0x3C9;
color_t colors [ 256 ];
int i;
memset ( colors, 0, sizeof (colors) );
for ( i = 0; i < 64; ++ i )
{
colors [i+1].r = (unsigned char) i;
colors [i+1].g = (unsigned char) (i * i / 63);
colors [i+1].b = (unsigned char) (i * i / 63);
}
colors [255].r = 16;
colors [255].g = 63;
colors [255].b = 0;
for ( i = 0; i < 256; ++ i )
{
outp ( DAC_write_index, i );
outp ( DAC_data_index, colors [i].r );
outp ( DAC_data_index, colors [i].g );
outp ( DAC_data_index, colors [i].b );
}
}
//----------------------------------------------------------------------------
// FUNCTION interact
//----------------------------------------------------------------------------
enum view_t
{
cockpit_view,
map_view
};
static void interact
(
void
)
{
const clearance = 8 * scale_height;
const max_altitude = (max_alt+4) * scale_height;
// Initialize the palette.
set_palette ();
// Store the mouse coordinates so we can tell later if it has moved.
mouse.update ();
int old_mouse_x = mouse.x;
int old_mouse_y = mouse.y;
// Initialize the player's position, heading and velocities.
viewer.x = 4 * scale_area;
viewer.y = 4 * scale_area;
viewer.z = color_map [4][4] * scale_height + clearance;
viewer.angle = 1800;
int vel = 0; // Forward velocity
int vz = 0; // Vertical velocity
int va = 0; // Angular velocity
// Initialize other variables.
view_t view = cockpit_view;
boolean showing_mouse = false;
boolean buttons_used = false;
boolean done = false;
//-------------------------------------------------------------------------
// The main interaction loop:
//-------------------------------------------------------------------------
while ( ! done )
{
//----------------------------------------------------------------------
// Update the player's state.
//----------------------------------------------------------------------
// Heading
viewer.angle += va;
while ( viewer.angle < 0 ) viewer.angle += 3600;
while ( viewer.angle >= 3600 ) viewer.angle -= 3600;
FIX_cos_sin ( viewer.angle, & viewer.cosine, & viewer.sine );
// Position
int vx = mul_and_div ( vel, -viewer.sine, 65536 );
int vy = mul_and_div ( vel, viewer.cosine, 65536 );
int map_x = (int) (viewer.x / scale_area);
int map_y = (int) (viewer.y / scale_area);
viewer.x += vx;
viewer.y += vy;
while ( viewer.x < 0 ) viewer.x += world_size_x;
while ( viewer.x >= world_size_x ) viewer.x -= world_size_x;
while ( viewer.y < 0 ) viewer.y += world_size_y;
while ( viewer.y >= world_size_y ) viewer.y -= world_size_y;
// Elevation
viewer.z += vz;
if ( viewer.z < alt_map [map_x][map_y] * scale_height + clearance )
{
viewer.z = alt_map [map_x][map_y] * scale_height + clearance;
vz = 0;
}
else if ( viewer.z > max_altitude )
{
viewer.z = max_altitude;
vz = 0;
}
//----------------------------------------------------------------------
// Draw the view.
//----------------------------------------------------------------------
if ( view == cockpit_view ) view_3d ();
else view_overhead_map ();
if ( showing_mouse )
{
win.rect ( 0, mouse.y-2, 3, mouse.y+3, black );
win.rect ( 0, mouse.y-1, 2, mouse.y+2, cursor_color);
win.rect ( 317, mouse.y-2, 320, mouse.y+3, black );
win.rect ( 318, mouse.y-1, 320, mouse.y+2, cursor_color);
win.rect ( mouse.x-2, 0, mouse.x+3, 3, black );
win.rect ( mouse.x-1, 0, mouse.x+2, 2, cursor_color);
}
vga.update ();
//----------------------------------------------------------------------
// Handle keyboard input.
//----------------------------------------------------------------------
while ( kbhit () )
{
int key = tolower (getch ());
if ( key == 0 )
{
key = getch ();
switch ( key )
{
case up_arrow_key : vz += scale_height / 2; break;
case dn_arrow_key : vz -= scale_height / 2; break;
case lt_arrow_key : va -= 5; break;
case rt_arrow_key : va += 5; break;
case center_key : vel = 0; va = 0; vz = 0; break;
default: break;
}
}
else switch ( key )
{
case esc_key: // Drop through
case 'q': done = true; break;
case 'm':
view = (view == cockpit_view) ? map_view : cockpit_view;
break;
case 'n': viewer.angle = 0; break;
case 'e': viewer.angle = 900; break;
case 's': viewer.angle = 1800; break;
case 'w': viewer.angle = 2700; break;
case 'r': viewer.angle = (viewer.angle + 1800) % 3600; break;
case '+': // Drop through
case '=': vel -= scale_area / 8; break;
case '-': vel += scale_area / 8; break;
default: break;
}
showing_mouse = false;
}
//----------------------------------------------------------------------
// Handle mouse input.
//----------------------------------------------------------------------
if ( mouse.exists )
{
mouse.update ();
if ( mouse.x != old_mouse_x || mouse.y != old_mouse_y )
{
showing_mouse = true;
old_mouse_x = mouse.x;
old_mouse_y = mouse.y;
va = mouse.x - 160;
vz = mouse.y - 100;
// Give the mouse an exponential response curve.
if (va < 0) va = va * va / -160;
else va = va * va / 160;
if (vz < 0) vz = vz * vz / (scale_height / 16);
else vz = vz * vz / -(scale_height / 16);
}
if ( mouse.buttons ) // If any button is down
{
if ( ! buttons_used )
{
view = (view == cockpit_view) ? map_view : cockpit_view;
buttons_used = true;
}
}
else
{
buttons_used = false;
}
}
}
}
//----------------------------------------------------------------------------
// FUNCTION check_equipment
//----------------------------------------------------------------------------
// Check for a mouse.
//----------------------------------------------------------------------------
static void check_equipment ( void )
{
mouse.init ();
if ( mouse.exists )
{
printf ( "Mouse detected.\n" );
}
else
{
printf ( "No mouse detected.\n" );
}
if ( ! vga.exists () )
{
fprintf ( stderr, "This program requires a VGA graphics adaptor.\n" );
exit (1);
}
}
//----------------------------------------------------------------------------
// FUNCTION main
//----------------------------------------------------------------------------
// Sets up and takes down the game environment. Checks first to be sure
// the necessary equipment is available, then changes screen modes before
// passing control to the interactive game-playing module. Upon regaining
// control, main resets the screen to its previous state.
//----------------------------------------------------------------------------
const char * instructions =
"\n\"Moonbase\" 3D Landscape Demo\n"
"────────────────────────────\n\n"
"Use the mouse or arrow keys to steer. Other keys are:\n\n"
" + - Increase/Decrease forward velocity\n"
" m Toggle satellite view\n"
" Esc Quit\n"
"\nPress Enter to begin.\n"
"";
const char * closing_msg =
"Moonbase was written by James McNeill (mcneja@wwc.edu)\n"
"using Watcom C++ and the PMODE/W DOS extender.\n";
int main
(
void
)
{
check_equipment ();
WORLD_generate ();
printf ( instructions );
if ( getch () == 0 ) getch ();
vga.start ();
interact ();
vga.end ();
printf ( closing_msg );
return 0;
}